﻿using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using NUnit.Framework;
using ProjectEuler.Core.Helpers;

namespace ProjectEuler.Core
{
    public class Problem33 : IProjectEulerProblem
    {
        public int Number
        {
            get { return 33; }
        }

        public string Description
        {
            get { return "Find the value of the denominator"; }
        }

        public string Answer
        {
            get
            {
                var fractions = GetAll2DigitFractions().Where(IsNonTrivialSimplifiable).ToList();

                var num = new BigInteger(1);
                var denom = new BigInteger(1);
                foreach (var fraction in fractions)
                {
                    num *= fraction.Numerator;
                    denom *= fraction.Denominator;
                }

                var f = new Fraction(num, denom);
                f.Simplify();

                return f.Denominator.ToString();
            }
        }

        protected IEnumerable<Fraction> GetAll2DigitFractions()
        {
            for (int i = 10; i <= 99; i++)
            {
                for (int j = (i + 1); j < 99; j++)
                {
                    yield return new Fraction(i, j);
                }
            }
        }

        public bool IsNonTrivialSimplifiable(Fraction value)
        {
            // trivial cases where 0's can be crossed out
            if ((value.Numerator % 10 == 0) && (value.Denominator % 10 == 0))
            {
                return false;
            }
        
            string numeratorString = value.Numerator.ToString();
            string denominatorString = value.Denominator.ToString();
        
            // cases where the numerator/denominator don't share any common numbers
            if (!denominatorString.Contains(numeratorString[0]) && !denominatorString.Contains(numeratorString[1]))
            {
                return false;
            }
        
            int crossOutNum;
            if (denominatorString.Contains(numeratorString[0]))
            {
                crossOutNum = int.Parse(numeratorString[0].ToString());
            }
            else
            {
                crossOutNum = int.Parse(numeratorString[1].ToString());
            }
        
            return (Fraction.AreEquivalent(value, CrossOutNumber(value, crossOutNum)));
        }

        public Fraction CrossOutNumber(Fraction f, int crossOutNum)
        {
            int num;
            int.TryParse(f.Numerator.ToString().Replace(crossOutNum.ToString(), ""), out num);
            if (num == 0) num = crossOutNum;
        
            int denom;
            int.TryParse(f.Denominator.ToString().Replace(crossOutNum.ToString(), ""), out denom);
            if (denom == 0) denom = crossOutNum;
        
            return new Fraction(num, denom);
        }    
    }

    [TestFixture]
    public class Problem33Tests : Problem33
    {
        [Test]
        public void The_49_98_fraction_should_be_in_the_list()
        {
            var fractions = GetAll2DigitFractions().ToList();

            Assert.IsTrue(fractions.Any(frac => frac.Numerator == 49 && frac.Denominator == 98));
        }

        [Test]
        public void The_49_98_fraction_should_be_nontrivial_simplifiable()
        {
            var fraction = new Fraction(49, 98);

            Assert.IsTrue(IsNonTrivialSimplifiable(fraction));
        }

        [Test]
        public void There_are_4_nontrivial_simplifiables()
        {
            var fractions = GetAll2DigitFractions().Where(IsNonTrivialSimplifiable).ToList();

            Assert.AreEqual(4, fractions.Count);
        }
    }
}
